// 版权归GoFrame作者(https://goframe.org)所有。保留所有权利。
//
// 本源代码形式受MIT许可证条款约束。
// 如果未随本文件一同分发MIT许可证副本，
// 您可以在https://github.com/gogf/gf处获取。
// md5:a9832f33b234e3f3

package 转换类

import (
	"reflect"
	"strings"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"gitee.com/go_888/extends/goframe/internal/empty"
	"gitee.com/go_888/extends/goframe/internal/json"
	"gitee.com/go_888/extends/goframe/internal/utils"
	"gitee.com/go_888/extends/goframe/gconv/internal/localinterface"
	"gitee.com/go_888/extends/goframe/gconv/internal/structcache"
)

// 结构体将参数的键值对映射到对应结构对象的属性。
// 第三个参数 `mapping` 不必要，表示自定义键名和属性名之间的映射规则（区分大小写）。
// 
// 注意：
// 1. `params` 可以是任何类型的 map/struct，通常为 map。
// 2. `pointer` 应该是 *struct/**struct 类型，即指向结构体对象或结构体指针。
// 3. 只有结构体对象的公共属性可以被映射。
// 4. 如果 `params` 是一个 map，其键 `params` 可以是小写。在映射过程中，它会自动将键的首字母转换为大写进行匹配。如果键不匹配，它将忽略该键。
// md5:b39a46da903b06f5
func X取结构(待转换值 interface{}, 变量指针 interface{}, 名称映射Map ...map[string]string) (错误 error) {
	return X取结构或结构数组(待转换值, 变量指针, 名称映射Map...)
}

// StructTag acts as Struct but also with support for priority tag feature, which retrieves the
// specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping.
// The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','.
func X取结构并按标签(待转换值 interface{}, 变量指针 interface{}, 优先级标签 string) (错误 error) {
	return doStruct(待转换值, 变量指针, nil, 优先级标签)
}

// doStruct 是将任何数据转换为结构体的核心内部函数。 md5:43cdc6b6cc398c7c
func doStruct(
	params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string,
) (err error) {
	if params == nil {
						// 如果`params`为nil，则不进行转换。 md5:0520708a0e7e1c1d
		return nil
	}
	if pointer == nil {
		return gerror.NewCode(gcode.CodeInvalidParameter, "对象指针不能为nil")
	}

				// JSON内容转换。 md5:8a29b5a7aa430047
	ok, err := doConvertWithJsonCheck(params, pointer)
	if err != nil {
		return err
	}
	if ok {
		return nil
	}

	defer func() {
						// 捕获panic，尤其是反射操作引发的panic。 md5:dd183bf8028f513a
		if exception := recover(); exception != nil {
			if v, ok := exception.(error); ok && gerror.HasStack(v) {
				err = v
			} else {
				err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
			}
		}
	}()

	var (
		paramsReflectValue      reflect.Value
		paramsInterface         interface{} // 不要直接使用`params`，因为它可能是`reflect.Value`类型. md5:f469653f5ba4e08c
		pointerReflectValue     reflect.Value
		pointerReflectKind      reflect.Kind
		pointerElemReflectValue reflect.Value // The reflection value to struct element.
	)
	if v, ok := params.(reflect.Value); ok {
		paramsReflectValue = v
	} else {
		paramsReflectValue = reflect.ValueOf(params)
	}
	paramsInterface = paramsReflectValue.Interface()
	if v, ok := pointer.(reflect.Value); ok {
		pointerReflectValue = v
		pointerElemReflectValue = v
	} else {
		pointerReflectValue = reflect.ValueOf(pointer)
		pointerReflectKind = pointerReflectValue.Kind()
		if pointerReflectKind != reflect.Ptr {
			return gerror.NewCodef(
				gcode.CodeInvalidParameter,
				"目标指针应为 '*struct' 类型，但实际得到的是 '%v'",
				pointerReflectKind,
			)
		}
						// 使用 IsNil 检查 reflect.Ptr 类型的变量是可行的。 md5:0ba920ba8a6a19cf
		if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() {
			return gerror.NewCode(
				gcode.CodeInvalidParameter,
				"目标指针不能为nil",
			)
		}
		pointerElemReflectValue = pointerReflectValue.Elem()
	}

	// 如果`params`和`pointer`是相同类型，直接进行赋值操作。
	// 为了性能优化。
	// md5:87eefbed1426eef0
	if ok = doConvertWithTypeCheck(paramsReflectValue, pointerElemReflectValue); ok {
		return nil
	}

	// custom convert.
	if ok, err = callCustomConverter(paramsReflectValue, pointerReflectValue); ok {
		return err
	}

			// 通常的接口解码检查。 md5:838cb73b6b92dc54
	if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
		return err
	}

	// 如果必要，它会自动创建结构体对象。
	// 例如，如果`pointer`是**User（双星号表示指针），那么`elem`就是*User，即User类型的指针。
	// md5:172757349701f610
	if pointerElemReflectValue.Kind() == reflect.Ptr {
		if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() {
			e := reflect.New(pointerElemReflectValue.Type().Elem())
			pointerElemReflectValue.Set(e)
			defer func() {
				if err != nil {
															// 如果转换失败，它将`pointer`重置为nil。 md5:52f95bfcfceeefc0
					pointerReflectValue.Elem().Set(reflect.Zero(pointerReflectValue.Type().Elem()))
				}
			}()
		}
		// if v, ok := pointerElemReflectValue.Interface().(localinterface.IUnmarshalValue); ok {
		//	return v.UnmarshalValue(params)
		// }
		// Note that it's `pointerElemReflectValue` here not `pointerReflectValue`.
		if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
			return err
		}
						// 获取其元素，可能是最后的结构体。 md5:4a887dcf759fad9d
		pointerElemReflectValue = pointerElemReflectValue.Elem()
	}
	paramsMap, ok := paramsInterface.(map[string]interface{})
	if !ok {
		// paramsMap is the map[string]interface{} type variable for params.
		// DO NOT use MapDeep here.
		paramsMap = doMapConvert(paramsInterface, recursiveTypeAuto, true)
		if paramsMap == nil {
			return gerror.NewCodef(
				gcode.CodeInvalidParameter,
				`将参数从 "%#v" 转换为 "map[string]interface{}" 失败`,
				params,
			)
		}
	}
			// 由于参数为空，无需进行任何操作。 md5:958747d8f67e1e73
	if len(paramsMap) == 0 {
		return nil
	}
	// Get struct info from cache or parse struct and cache the struct info.
	cachedStructInfo := structcache.GetCachedStructInfo(
		pointerElemReflectValue.Type(), priorityTag,
	)
			// 没有需要转换的内容。 md5:68441f55873cce91
	if cachedStructInfo == nil {
		return nil
	}
	// For the structure types of 0 tagOrFiledNameToFieldInfoMap,
	// they also need to be cached to prevent invalid logic
	if cachedStructInfo.HasNoFields() {
		return nil
	}
	var (
						// 表示这些值已被使用，不能重复使用。 md5:66845c8e5a8adbe8
		usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool()
		cachedFieldInfo           *structcache.CachedFieldInfo
		paramsValue               interface{}
	)
	defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap)

	// 首先，根据自定义的映射规则进行搜索。
	// 如果找到了可能的直接赋值关系，减少后续映射搜索的数量。
	// md5:50dd567944f99367
	for paramKey, fieldName := range paramKeyToAttrMap {
		paramsValue, ok = paramsMap[paramKey]
		if !ok {
			continue
		}
		cachedFieldInfo = cachedStructInfo.GetFieldInfo(fieldName)
		if cachedFieldInfo != nil {
			fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue)
			if err = bindVarToStructField(
				fieldValue,
				paramsValue,
				cachedFieldInfo,
				paramKeyToAttrMap,
			); err != nil {
				return err
			}
			if len(cachedFieldInfo.OtherSameNameField) > 0 {
				if err = setOtherSameNameField(
					cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
				); err != nil {
					return err
				}
			}
			usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
		}
	}
	// Already done converting for given `paramsMap`.
	if len(usedParamsKeyOrTagNameMap) == len(paramsMap) {
		return nil
	}
	return bindStructWithLoopFieldInfos(
		paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
	)
}

func setOtherSameNameField(
	cachedFieldInfo *structcache.CachedFieldInfo,
	srcValue any,
	structValue reflect.Value,
	paramKeyToAttrMap map[string]string,
) (err error) {
	// loop the same field name of all sub attributes.
	for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField {
		fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes)
		if err = bindVarToStructField(fieldValue, srcValue, otherFieldInfo, paramKeyToAttrMap); err != nil {
			return err
		}
	}
	return nil
}

func bindStructWithLoopFieldInfos(
	paramsMap map[string]any,
	structValue reflect.Value,
	paramKeyToAttrMap map[string]string,
	usedParamsKeyOrTagNameMap map[string]struct{},
	cachedStructInfo *structcache.CachedStructInfo,
) (err error) {
	var (
		cachedFieldInfo *structcache.CachedFieldInfo
		fuzzLastKey     string
		fieldValue      reflect.Value
		paramKey        string
		paramValue      any
		matched         bool
		ok              bool
	)
	for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos {
		for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName {
			if paramValue, ok = paramsMap[fieldTag]; !ok {
				continue
			}
			fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
			if err = bindVarToStructField(
				fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
			); err != nil {
				return err
			}
			// handle same field name in nested struct.
			if len(cachedFieldInfo.OtherSameNameField) > 0 {
				if err = setOtherSameNameField(
					cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
				); err != nil {
					return err
				}
			}
			usedParamsKeyOrTagNameMap[fieldTag] = struct{}{}
			matched = true
			break
		}
		if matched {
			matched = false
			continue
		}

		fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
		if paramValue, ok = paramsMap[fuzzLastKey]; !ok {
			paramKey, paramValue = fuzzyMatchingFieldName(
				cachedFieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap,
			)
			ok = paramKey != ""
			cachedFieldInfo.LastFuzzyKey.Store(paramKey)
		}
		if ok {
			fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
			if paramValue != nil {
				if err = bindVarToStructField(
					fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
				); err != nil {
					return err
				}
				// handle same field name in nested struct.
				if len(cachedFieldInfo.OtherSameNameField) > 0 {
					if err = setOtherSameNameField(
						cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
					); err != nil {
						return err
					}
				}
			}
			usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
		}
	}
	return nil
}

// 模糊匹配规则：
// 不区分大小写，不考虑符号地匹配字段名和参数键。
// md5:22c4645c8af23d0d
func fuzzyMatchingFieldName(
	fieldName string,
	paramsMap map[string]any,
	usedParamsKeyMap map[string]struct{},
) (string, any) {
	for paramKey, paramValue := range paramsMap {
		if _, ok := usedParamsKeyMap[paramKey]; ok {
			continue
		}
		removeParamKeyUnderline := utils.RemoveSymbols(paramKey)
		if strings.EqualFold(fieldName, removeParamKeyUnderline) {
			return paramKey, paramValue
		}
	}
	return "", nil
}

// bindVarToStructField sets value to struct object attribute by name.
func bindVarToStructField(
	fieldValue reflect.Value,
	srcValue interface{},
	cachedFieldInfo *structcache.CachedFieldInfo,
	paramKeyToAttrMap map[string]string,
) (err error) {
	if !fieldValue.IsValid() {
		return nil
	}
			// CanSet 检查该属性是否可以公开访问。 md5:fafe4f3a8bd7621f
	if !fieldValue.CanSet() {
		return nil
	}
	defer func() {
		if exception := recover(); exception != nil {
			if err = bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil {
				err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName())
			}
		}
	}()
	// Directly converting.
	if empty.IsNil(srcValue) {
		fieldValue.Set(reflect.Zero(fieldValue.Type()))
		return nil
	}
	// Try to call custom converter.
	// Issue: https://github.com/gogf/gf/issues/3099
	var (
		customConverterInput reflect.Value
		ok                   bool
	)
	if cachedFieldInfo.IsCustomConvert {
		if customConverterInput, ok = srcValue.(reflect.Value); !ok {
			customConverterInput = reflect.ValueOf(srcValue)
		}
		if ok, err = callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
			return
		}
	}
	if cachedFieldInfo.IsCommonInterface {
		if ok, err = bindVarToReflectValueWithInterfaceCheck(fieldValue, srcValue); ok || err != nil {
			return
		}
	}
	// Common types use fast assignment logic
	if cachedFieldInfo.ConvertFunc != nil {
		cachedFieldInfo.ConvertFunc(srcValue, fieldValue)
		return nil
	}
	doConvertWithReflectValueSet(fieldValue, doConvertInput{
		FromValue:  srcValue,
		ToTypeName: cachedFieldInfo.StructField.Type.String(),
		ReferValue: fieldValue,
	})
	return nil
}

// bindVarToReflectValueWithInterfaceCheck 使用通用接口检查进行绑定。 md5:ede209e9eacebf79
func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (bool, error) {
	var pointer interface{}
	if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
		reflectValueAddr := reflectValue.Addr()
		if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() {
			return false, nil
		}
						// 不是指针，但可以处理地址，因此它可以被反序列化。 md5:52a739dbed72b8c0
		pointer = reflectValue.Addr().Interface()
	} else {
		if reflectValue.IsNil() || !reflectValue.IsValid() {
			return false, nil
		}
		pointer = reflectValue.Interface()
	}
	// UnmarshalValue.
	if v, ok := pointer.(localinterface.IUnmarshalValue); ok {
		return ok, v.UnmarshalValue(value)
	}
	// UnmarshalText.
	if v, ok := pointer.(localinterface.IUnmarshalText); ok {
		var valueBytes []byte
		if b, ok := value.([]byte); ok {
			valueBytes = b
		} else if s, ok := value.(string); ok {
			valueBytes = []byte(s)
		} else if f, ok := value.(localinterface.IString); ok {
			valueBytes = []byte(f.String())
		}
		if len(valueBytes) > 0 {
			return ok, v.UnmarshalText(valueBytes)
		}
	}
	// UnmarshalJSON.
	if v, ok := pointer.(localinterface.IUnmarshalJSON); ok {
		var valueBytes []byte
		if b, ok := value.([]byte); ok {
			valueBytes = b
		} else if s, ok := value.(string); ok {
			valueBytes = []byte(s)
		} else if f, ok := value.(localinterface.IString); ok {
			valueBytes = []byte(f.String())
		}

		if len(valueBytes) > 0 {
									// 如果它不是一个有效的JSON字符串，那么就在它的两边添加字符 `"` 以使其成为有效JSON字符串。 md5:d6a38f1500604604
			if !json.Valid(valueBytes) {
				newValueBytes := make([]byte, len(valueBytes)+2)
				newValueBytes[0] = '"'
				newValueBytes[len(newValueBytes)-1] = '"'
				copy(newValueBytes[1:], valueBytes)
				valueBytes = newValueBytes
			}
			return ok, v.UnmarshalJSON(valueBytes)
		}
	}
	if v, ok := pointer.(localinterface.ISet); ok {
		v.X设置值(value)
		return ok, nil
	}
	return false, nil
}

// bindVarToReflectValue 将 `value` 设置为反射值对象 `structFieldValue`。 md5:c78b60ec569060eb
func bindVarToReflectValue(
	structFieldValue reflect.Value, value interface{}, paramKeyToAttrMap map[string]string,
) (err error) {
				// JSON内容转换。 md5:8a29b5a7aa430047
	ok, err := doConvertWithJsonCheck(value, structFieldValue)
	if err != nil {
		return err
	}
	if ok {
		return nil
	}

	kind := structFieldValue.Kind()
			// 使用`Set`接口实现转换，对于某些类型。 md5:51e8e3ad23771259
	switch kind {
	case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface:
		if !structFieldValue.IsNil() {
			if v, ok := structFieldValue.Interface().(localinterface.ISet); ok {
				v.X设置值(value)
				return nil
			}
		}
	}

			// 使用反射按类型进行转换。 md5:e3c406f111505fd2
	switch kind {
	case reflect.Map:
		return doMapToMap(value, structFieldValue, paramKeyToAttrMap)

	case reflect.Struct:
						// 递归转换结构体属性。 md5:ae6513ef6e56f654
		if err = doStruct(value, structFieldValue, nil, ""); err != nil {
												// 请注意这里存在反射转换机制。 md5:84599bf48af19237
			structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
		}

	// 注意，切片元素的类型可能是结构体，
	// 因此它内部使用了一个名为Struct的函数来进行转换。
	// md5:b8519d4d1a736c40
	case reflect.Slice, reflect.Array:
		var (
			reflectArray reflect.Value
			reflectValue = reflect.ValueOf(value)
		)
		if reflectValue.Kind() == reflect.Slice || reflectValue.Kind() == reflect.Array {
			reflectArray = reflect.MakeSlice(structFieldValue.Type(), reflectValue.Len(), reflectValue.Len())
			if reflectValue.Len() > 0 {
				var (
					elemType     = reflectArray.Index(0).Type()
					elemTypeName string
					converted    bool
				)
				for i := 0; i < reflectValue.Len(); i++ {
					converted = false
					elemTypeName = elemType.Name()
					if elemTypeName == "" {
						elemTypeName = elemType.String()
					}
					var elem reflect.Value
					if elemType.Kind() == reflect.Ptr {
						elem = reflect.New(elemType.Elem()).Elem()
					} else {
						elem = reflect.New(elemType).Elem()
					}
					if elem.Kind() == reflect.Struct {
						if err = doStruct(reflectValue.Index(i).Interface(), elem, nil, ""); err == nil {
							converted = true
						}
					}
					if !converted {
						doConvertWithReflectValueSet(elem, doConvertInput{
							FromValue:  reflectValue.Index(i).Interface(),
							ToTypeName: elemTypeName,
							ReferValue: elem,
						})
					}
					if elemType.Kind() == reflect.Ptr {
																		// 在将`elem`设置为数组之前，如果必要的话进行指针转换。 md5:1466632fc1d552e6
						elem = elem.Addr()
					}
					reflectArray.Index(i).Set(elem)
				}
			}
		} else {
			var (
				elem         reflect.Value
				elemType     = structFieldValue.Type().Elem()
				elemTypeName = elemType.Name()
				converted    bool
			)
			switch reflectValue.Kind() {
			case reflect.String:
				// Value is empty string.
				if reflectValue.IsZero() {
					var elemKind = elemType.Kind()
															// 尝试找到切片元素的原始类型类别。 md5:903e45eb4bc9a592
					if elemKind == reflect.Ptr {
						elemKind = elemType.Elem().Kind()
					}
					switch elemKind {
					case reflect.String:
																		// 空字符串不能赋值给字符串切片。 md5:7015d8a83525c473
						return nil
					}
				}
			}
			if elemTypeName == "" {
				elemTypeName = elemType.String()
			}
			if elemType.Kind() == reflect.Ptr {
				elem = reflect.New(elemType.Elem()).Elem()
			} else {
				elem = reflect.New(elemType).Elem()
			}
			if elem.Kind() == reflect.Struct {
				if err = doStruct(value, elem, nil, ""); err == nil {
					converted = true
				}
			}
			if !converted {
				doConvertWithReflectValueSet(elem, doConvertInput{
					FromValue:  value,
					ToTypeName: elemTypeName,
					ReferValue: elem,
				})
			}
			if elemType.Kind() == reflect.Ptr {
																// 在将`elem`设置为数组之前，如果必要的话进行指针转换。 md5:1466632fc1d552e6
				elem = elem.Addr()
			}
			reflectArray = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
			reflectArray.Index(0).Set(elem)
		}
		structFieldValue.Set(reflectArray)

	case reflect.Ptr:
		if structFieldValue.IsNil() || structFieldValue.IsZero() {
									// 如果是空指针或空列表，它会创建一个新的。 md5:a005c5e6ed40f663
			item := reflect.New(structFieldValue.Type().Elem())
			if ok, err = bindVarToReflectValueWithInterfaceCheck(item, value); ok {
				structFieldValue.Set(item)
				return err
			}
			elem := item.Elem()
			if err = bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil {
				structFieldValue.Set(elem.Addr())
			}
		} else {
									// 非空指针，它会给它赋值。 md5:2bd4c15a81dcbdcf
			return bindVarToReflectValue(structFieldValue.Elem(), value, paramKeyToAttrMap)
		}

			// 它主要且特别地处理了nil值的接口。 md5:0c8e2dd31d82d96e
	case reflect.Interface:
		if value == nil {
			// Specially.
			structFieldValue.Set(reflect.ValueOf((*interface{})(nil)))
		} else {
												// 请注意这里存在反射转换机制。 md5:84599bf48af19237
			structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
		}

	default:
		defer func() {
			if exception := recover(); exception != nil {
				err = gerror.NewCodef(
					gcode.CodeInternalPanic,
					`无法将值 "%+v" 转换为类型 "%s": %+v`,
					value,
					structFieldValue.Type().String(),
					exception,
				)
			}
		}()
		// 此处使用反射将`value`转换为属性的类型，然后将结果值赋给该属性。
		// 如果常规的Go转换规则不允许转换，此操作可能会失败并引发恐慌。
		// md5:931b86f723a12b7c
		structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
	}
	return nil
}
